package com.atguigu.tingshu.search.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsAggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.aggregations.TopHitsAggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.*;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {


    @Autowired
    private AlbumInfoIndexRepository albumInfoIndexRepository;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private ElasticsearchClient elasticSearchClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /*
    方法一：使用@Resource的目的是为了告诉Spring具体要注入哪一个bean对象
    方法二：或者也可以使用@Autowired+@Qualifier联合使用也可以
    方法三：只使用@Autowiredm,但是变量名需要和函数名一致
     */
    @Resource(name = "threadPoolTaskExecutor")
    private Executor threadPoolTaskExecutor;

    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    private final static String INDEX_NAME = "albuminfo";

    private final static String SUGGEST_INDEX_NAME = "suggestinfo";

    /**
     * 将指定专辑保存到索引库中
     *
     * @param albumId
     */
    @Override
    public void upperAlbum(Long albumId) {
        //1.初始化专辑索引库文档对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        //2.封装专辑及标签信息-远程调用专辑服务获取专辑相关信息
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumId, "专辑{}不存在", albumId);

            //2.1 将专辑基本信息拷贝
            BeanUtil.copyProperties(albumInfo, albumInfoIndex);

            //2.2 封装专辑标签列表信息
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            if (CollectionUtil.isNotEmpty(albumAttributeValueVoList)) {
                List<AttributeValueIndex> attributeValueIndexList = albumAttributeValueVoList.stream()
                        .map(albumAttributeValue -> {
                            return BeanUtil.copyProperties(albumAttributeValue, AttributeValueIndex.class);
                        }).collect(Collectors.toList());
                albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);

            }
            return albumInfo;
        }, threadPoolTaskExecutor);

        CompletableFuture<Void> categoryCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            //3.封装专辑分类信息-远程调用专辑服务根据三级id获取专辑分类信息
            Long category3Id = albumInfo.getCategory3Id();
            BaseCategoryView baseCategoryView = albumFeignClient.getCategoryView(category3Id).getData();
            Assert.notNull(baseCategoryView, "专辑{}所属分类{}不存在", albumId, category3Id);
            //注意：这里不能再拷贝了，因为用户id会覆盖专辑id
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
            albumInfoIndex.setCategory3Id(baseCategoryView.getCategory3Id());
        }, threadPoolTaskExecutor);

        CompletableFuture<Void> userCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            //4.封装主播信息-远程调用用户服务根据用户id查询主播信息
            Long userId = albumInfo.getUserId();
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "专辑{}所属主播{}不存在", albumId, userId);
            //注意：同理，这里不能再拷贝了，因为用户id会覆盖专辑id
            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
        }, threadPoolTaskExecutor);


        //5.封装专辑统计信息，采用随机生成数值，后续改为远程调用专辑服务获取专辑统计信息。
        CompletableFuture<Void> statCompletableFuture = CompletableFuture.runAsync(() -> {
            //5.1 随机产生四项统计数值
            int viewNum = RandomUtil.randomInt(1000, 2000);
            int subscriptionNum = RandomUtil.randomInt(1000, 1500);
            int purchaseNum = RandomUtil.randomInt(500, 1000);
            int commentNum = RandomUtil.randomInt(300, 800);

            albumInfoIndex.setPlayStatNum(viewNum);
            albumInfoIndex.setSubscribeStatNum(subscriptionNum);
            albumInfoIndex.setBuyStatNum(purchaseNum);
            albumInfoIndex.setCommentStatNum(commentNum);

            //5.2 基于四项统计数值分别设置不同的权重，最终算出专辑热度值
            BigDecimal bigDecimal1 = BigDecimal.valueOf(viewNum).multiply(BigDecimal.valueOf(0.1));
            BigDecimal bigDecimal2 = BigDecimal.valueOf(subscriptionNum).multiply(BigDecimal.valueOf(0.2));
            BigDecimal bigDecimal3 = BigDecimal.valueOf(purchaseNum).multiply(BigDecimal.valueOf(0.3));
            BigDecimal bigDecimal4 = BigDecimal.valueOf(commentNum).multiply(BigDecimal.valueOf(0.4));


            double hotScore = bigDecimal1
                    .add(bigDecimal2)
                    .add(bigDecimal3)
                    .add(bigDecimal4)
                    .doubleValue();
            albumInfoIndex.setHotScore(hotScore);

        }, threadPoolTaskExecutor);

        //6.组合所有的异步任务，等待所有的异步任务都执行完毕，才进行下一步
        CompletableFuture.allOf(albumInfoCompletableFuture,
                categoryCompletableFuture,
                userCompletableFuture,
                statCompletableFuture
        ).join();
        albumInfoIndexRepository.save(albumInfoIndex);

        //7.TODO 将专辑标题存入提词索引库中
        this.saveSuggestDoc(albumInfoIndex);

        //8.将上架的专辑id存入到布隆过滤器中
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        bloomFilter.add(albumId);
    }

    /**
     * 将指定专辑从索引库中下架
     *
     * @param albumId
     */
    @Override
    public void lowerAlbum(Long albumId) {
        albumInfoIndexRepository.deleteById(albumId);
        //删除对应文档的提词记录
        suggestIndexRepository.deleteById(albumId.toString());

    }

    /**
     * 站内专辑搜索
     *
     * @param albumIndexQuery 查询条件对象
     * @return
     */
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {
        try {
            //一、构建用于检索请求对象
            SearchRequest searchRequest = this.buildDSL(albumIndexQuery);
            log.info("本次检索DSL：");
            System.err.println("searchRequest = " + searchRequest);

            //二、执行检索
            SearchResponse<AlbumInfoIndex> searchResponse = elasticSearchClient.search(searchRequest, AlbumInfoIndex.class);

            //三、解析ES响应结果
            return this.parseResult(searchResponse, albumIndexQuery);
        } catch (IOException e) {
            log.error("[搜索服务]站内搜索异常:{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取一级分类置顶七个三级分类下包含热度最高的六个专辑列表
     *
     * @param category1Id
     * @return
     */
    @Override
    public List<Map<String, Object>> getTop6(Long category1Id) {
        try {
            //1.远程调用专辑服务获取指定1级分类下置顶的7个三级分类
            List<BaseCategory3> baseCategory3List = albumFeignClient.findTop7BaseCategory3(category1Id).getData();
            Assert.notNull(baseCategory3List, "一级分类{}下没有三级分类", category1Id);

            //1.1 通过Stream流获取7个三级分类id列表
            List<Long> baseCategory3IdList = baseCategory3List.stream()
                    .map(BaseCategory3::getId)
                    .collect(Collectors.toList());

            //1.2 将Long类型转换为FiledValue类型
            List<FieldValue> fieldValueList = baseCategory3IdList.stream()
                    .map(baseCategory3Id -> {
                        return FieldValue.of(baseCategory3Id);
                    })
                    .collect(Collectors.toList());

            //1.3 封装结果时候需要根据三级分类id获取三级分类对象，将三级分类集合转为Map
            //key：三级分类id，value：三级分类对象
            Map<Long, BaseCategory3> category3Map = baseCategory3List
                    .stream()
                    .collect(Collectors.toMap(BaseCategory3::getId, baseCategory3 -> baseCategory3));


            //2.检索ES获取置顶3级分类下包含热度最高的6个专辑
//            SearchResponse<AlbumInfoIndex> searchResponse = elasticSearchClient.search(s -> {
//                s.index(INDEX_NAME)
//                        .query(q -> {
//                            q.terms(t -> {
//                                t.field("category3Id")
//                                        .terms(t1 -> {
//                                            t1.value(fieldValueList);
//                                            return t1;
//                                        });
//                                return t;
//                            });
//                            return q;
//                        })
//                        .size(0)
//                        .aggregations("category3Agg", a -> {
//                            a.terms(t -> {
//                                t.field("category3Id")
//                                        .size(10);
//                                return t;
//                            }).aggregations("top6", a1 -> {
//                                a1.topHits(t -> {
//                                    t.sort(sort -> {
//                                                sort.field(f -> {
//                                                    f.field("hotScore")
//                                                            .order(SortOrder.Desc);
//                                                    return f;
//                                                });
//
//                                                return sort;
//                                            }).size(6)
//                                            .source(s1 -> {
//                                                s1.filter(f -> {
//                                                    f.includes(
//                                                            "albumTitle",
//                                                            "payType",
//                                                            "includeTrackCount",
//                                                            "playStatNum",
//                                                            "coverUrl"
//                                                    );
//                                                    return f;
//                                                });
//                                                return s1;
//                                            });
//
//                                    return t;
//                                });
//
//                                return a1;
//                            });
//
//                            return a;
//                        });
//
//
//                return s;
//            }, AlbumInfoIndex.class);

            SearchResponse<AlbumInfoIndex> searchResponse = elasticSearchClient.search(s ->
                            s.index(INDEX_NAME)
                                    .query(q -> q.terms(t ->          // 简化query构建
                                            t.field("category3Id")
                                                    .terms(t1 -> t1.value(fieldValueList))
                                    ))
                                    .size(0)
                                    .aggregations("category3Agg", a ->
                                            a.terms(t -> t.field("category3Id").size(10))  // 主聚合：按三级分类分组
                                                    .aggregations("top6", a1 ->                  // 错误点：子聚合需用subAggregation
                                                            a1.topHits(th ->                         // 修正：子聚合需内嵌到terms聚合内部
                                                                    th.sort(sort -> sort.field(f -> f.field("hotScore").order(SortOrder.Desc)))
                                                                            .size(6)
                                                                            .source(sc -> sc.filter(f -> f.includes(  // 简化source过滤
                                                                                    //注意这里必须写上id
                                                                                    "id",
                                                                                    "albumTitle",
                                                                                    "payType",
                                                                                    "includeTrackCount",
                                                                                    "playStatNum",
                                                                                    "coverUrl",
                                                                                    "category1Id",
                                                                                    "category2Id",
                                                                                    "category3Id"
                                                                            )))
                                                            )
                                                    )
                                    ),
                    AlbumInfoIndex.class
            );

            //3.解析ES响应结果，封装置顶分类热门专辑Map对象，将Map对象封装为集合。
            System.out.println(searchResponse);

            //3.1 从响应对象中获取"category3Agg"集合结果对象
            LongTermsAggregate category3Agg = searchResponse.aggregations().get("category3Agg").lterms();

            //3.2 从聚合结果对象中获取"buckets"集合对象
            List<LongTermsBucket> categoryBuckets = category3Agg.buckets().array();
            if (CollectionUtil.isNotEmpty(categoryBuckets)) {
                //3.3 遍历bucket集合，获取每个bucket对象，获取三级分类ID以及该分类下的热门专辑。
                List<Map<String, Object>> list = categoryBuckets.stream()
                        .map(categoryBucket -> {
                            //3.3.1 获取三级分类id
                            long topCategory3Id = categoryBucket.key();

                            //3.3.2 进一步获取top子聚合
                            TopHitsAggregate top6Agg = categoryBucket.aggregations().get("top6").topHits();

                            //3.3.3 获取子聚合中的热门专辑
                            List<Hit<JsonData>> hitList = top6Agg.hits().hits();
                            if (CollectionUtil.isNotEmpty(hitList)) {
                                //3.3.4 遍历热门专辑返回专辑索引库对象
                                List<AlbumInfoIndex> top6AlbumList = hitList.stream()
                                        .map(hit -> {
                                            String hitSourceJsonStr = hit.source().toString();
                                            return JSON.parseObject(hitSourceJsonStr, AlbumInfoIndex.class);
                                        }).collect(Collectors.toList());

                                //3.4 构建置顶分类热门专辑Map对象
                                Map<String, Object> map = new HashMap<>();
                                //如何根据三级分类id获取分类对象
                                map.put("baseCategory3", category3Map.get(topCategory3Id));
                                map.put("list", top6AlbumList);
                                return map;
                            }
                            return null;
                        }).collect(Collectors.toList());
                return list;
            }

        } catch (IOException e) {
            log.error("[搜索服务]热门专辑检索异常:{}", e);
            throw new RuntimeException(e);
        }

        return null;

    }

    /**
     * 将专辑索引库中的标题存入提词索引库
     *
     * @param albumInfoIndex
     */
    @Override
    public void saveSuggestDoc(AlbumInfoIndex albumInfoIndex) {
        //1.创建提词文档对象
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(albumInfoIndex.getId().toString());
        String albumTitle = albumInfoIndex.getAlbumTitle();
        suggestIndex.setTitle(albumTitle);
        //用于汉字的自动补全
        suggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumTitle()}));
        //用于汉语拼音自动补全，将汉字转换为拼音
        String pinyin = PinyinUtil.getPinyin(albumTitle, "");
        suggestIndex.setKeywordPinyin(new Completion(new String[]{pinyin}));
        //用于汉语拼音首字母自动补全，将汉字转换为拼音首字母
        String firstLetter = PinyinUtil.getFirstLetter(albumTitle, "");
        suggestIndex.setKeywordSequence(new Completion(new String[]{firstLetter}));

        //2.保存提词文档对象
        suggestIndexRepository.save(suggestIndex);

    }

    /**
     * 搜索关键字自动补全
     *
     * @param keyword
     * @return
     */
    @Override
    public List<String> completeSuggest(String keyword) {
        try {
            //1.根据用户录入的内容检索提词索引库
            SearchResponse<SuggestIndex> searchResponse = elasticSearchClient.search(s -> s
                            .suggest(sb -> sb
                                    .suggesters("keyWordSuggest", sugg -> sugg
                                            .prefix(keyword)
                                            .completion(c -> c
                                                    .field("keyword")
                                                    .skipDuplicates(true)
                                                    .size(10)
                                            )
                                    )
                                    .suggesters("keyWordPinyinSuggest", sugg -> sugg
                                            .prefix(keyword)
                                            .completion(c -> c
                                                    .field("keywordPinyin")
                                                    .skipDuplicates(true)
                                                    .size(10)
                                            )
                                    )
                                    .suggesters("letterSuggest", sugg -> sugg
                                            .prefix(keyword)
                                            .completion(c -> c
                                                    .field("keywordSequence")
                                                    .skipDuplicates(true)
                                                    .size(10)
                                            )
                                    )
                            ),
                    SuggestIndex.class
            );

            //2.解析提词索引库检索结果放入HashSet中
            HashSet<String> hashSet = new HashSet<>();
            hashSet.addAll(this.parseSuggestResult(searchResponse, "keyWordSuggest"));
            hashSet.addAll(this.parseSuggestResult(searchResponse, "keyWordPinyinSuggest"));
            hashSet.addAll(this.parseSuggestResult(searchResponse, "letterSuggest"));

            //3.如果结果长度小于10，尝试根据文本进行全文查询补全到10个
            if (hashSet.size() < 10) {
                SearchResponse<AlbumInfoIndex> response = elasticSearchClient.search(s ->
                        s.index(INDEX_NAME).query(q ->
                                q.match(m ->
                                        m.field("albumTitle")
                                                .query(keyword))).size(10).source(s1 ->
                                s1.filter(f ->
                                        f.includes("albumTitle"))), AlbumInfoIndex.class);
                List<Hit<AlbumInfoIndex>> hitList = response.hits().hits();
                if (CollectionUtil.isNotEmpty(hitList)) {
                    for (Hit<AlbumInfoIndex> albumInfoIndexHit : hitList) {
                        String albumTitle = albumInfoIndexHit.source().getAlbumTitle();
                        hashSet.add(albumTitle);
                        if (hashSet.size() >= 10) {
                            break;
                        }
                    }
                }

            }

            //4.如果结果长度大于10最多返回10个
            if (hashSet.size() < 10) {
                return new ArrayList<>(hashSet);
            } else {
                return new ArrayList<>(hashSet).subList(0, 10);
            }
        } catch (IOException e) {
            log.error("[搜索服务]关键字自动补全异常:{}", e);
            throw new RuntimeException(e);
        }

    }

    /**
     * 从建议结果中获取建议词
     *
     * @param searchResponse
     * @param suggestName
     * @return
     */
    @Override
    public List<String> parseSuggestResult(SearchResponse<SuggestIndex> searchResponse, String suggestName) {
        List<String> result = new ArrayList<>();
        List<Suggestion<SuggestIndex>> suggestionList = searchResponse.suggest().get(suggestName);
        if (CollectionUtil.isNotEmpty(suggestionList)) {
            //解析获取自定义建议参数对应建议词结果
            for (Suggestion<SuggestIndex> suggestIndexSuggestion : suggestionList) {
                for (CompletionSuggestOption<SuggestIndex> option : suggestIndexSuggestion.completion().options()) {
                    SuggestIndex suggestIndex = option.source();
                    String title = suggestIndex.getTitle();
                    result.add(title);

                }
            }
        }
        return result;

    }

    /**
     * 统计ES中不同分类下不同维度的热度较高的专辑列表，将其存入Redis缓存中
     *
     * @return
     */
    @Override
    public void updateLatelyAlbumRanking() {
        try {
            //1.远程调用专辑服务获取一级分类列表，得到所有的以及分类id列表
            List<BaseCategory1> baseCategory1List = albumFeignClient.findAllCategory1().getData();
            albumFeignClient.findAllCategory1().getData();
            Assert.notNull(baseCategory1List, "一级分类列表为空");
        /*
        2.遍历一级分类列表，查询每个一级分类下，
        对应维度的热度较高的专辑top10列表，将结果写入Redis缓存中。
         */
            for (BaseCategory1 baseCategory1 : baseCategory1List) {
                Long category1Id = baseCategory1.getId();
                //2 声明存入Redi中的排行榜的key(标识分类)
                String key = RedisConstant.RANKING_KEY_PREFIX + category1Id;
                //2.1 每个分类下都有固定五中排序维度
                List<String> sortFieldList = Arrays.asList(
                        "hotScore",
                        "playStatNum",
                        "subscribeStatNum",
                        "buyStatNum",
                        "commentStatNum");

                //2.2 遍历排序维度列表，查询每个维度下top10的专辑列表
                for (String sortField : sortFieldList) {
                    //2.2.1 声明存入Redis中的排行榜的Field(标识排序字段)
                    String field = sortField;
                    //2.2.2 根据一级分类id和排序字段查询top10的专辑列表
                    SearchResponse<AlbumInfoIndex> searchResponse = elasticSearchClient
                            .search(s -> s.index(INDEX_NAME)
                                            .query(q -> q.term(t -> t.field("category1Id").value(category1Id)))
                                            .sort(s1 -> s1.field(f -> f.field(sortField).order(SortOrder.Desc)))
                                            .size(10)
                                            .source(s1 -> s1.filter(f -> f.includes(
                                                    "id",
                                                    "albumTitle",
                                                    "albumIntro",
                                                    "payType",
                                                    "includeTrackCount",
                                                    "playStatNum",
                                                    "coverUrl")))
                                    , AlbumInfoIndex.class);
                    //2.3 解析ES对象响应结果，并且将响应结果存入到Redis中。
                    List<Hit<AlbumInfoIndex>> hitList = searchResponse.hits().hits();
                    if (CollectionUtil.isNotEmpty(hitList)) {
                        List<AlbumInfoIndex> albumInfoIndexList = hitList.stream()
                                .map(hit -> hit.source())
                                .collect(Collectors.toList());
                        //2.4 将结果存入到Redis中
                        redisTemplate.opsForHash().put(key, field, albumInfoIndexList);

                    }

                }

            }
        } catch (IOException e) {
            log.error("[搜索服务]更新Redis小时排行榜异常：{}", e);
            throw new RuntimeException(e);
        }

    }

    /**
     * 根据一级分类id以及排序字段获取TOP10专辑列表
     *
     * @param category1Id
     * @param dimension
     * @return
     */
    @Override
    public List<AlbumInfoIndex> findRankingList(Long category1Id, String dimension) {
        //声明获取Redis中的排行榜的key（标识分类）
        String key = RedisConstant.RANKING_KEY_PREFIX + category1Id;
        //根据key+field获取hash结构中的value
        List<AlbumInfoIndex> list = (List<AlbumInfoIndex>) redisTemplate.opsForHash().get(key, dimension);
        return list;
    }

    /**
     * 解析ES检索结果，封装VO对象响应客户端
     *
     * @param searchResponse  ES响应结果对象
     * @param albumIndexQuery 查询条件对象
     * @return
     */
    private AlbumSearchResponseVo parseResult(SearchResponse<AlbumInfoIndex> searchResponse, AlbumIndexQuery albumIndexQuery) {
        //1.构建检索响应VO对象
        AlbumSearchResponseVo vo = new AlbumSearchResponseVo();

        //2.设置VO对象中分页相关信息
        //2.1 获取查询条件中的页码、页大小
        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();

        //2.2 解析ES响应结果获取总记录数
        HitsMetadata<AlbumInfoIndex> hits = searchResponse.hits();
        long total = hits.total().value();

        //2.3 计算总页码数
        long totalPages = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;

        //2.4 封装vo分页相关属性
        vo.setPageSize(pageSize);
        vo.setPageNo(pageNo);
        vo.setTotal(total);
        vo.setTotalPages(totalPages);

        //3.设置VO对象中检索到专辑列表-处理高亮
        List<Hit<AlbumInfoIndex>> hitList = hits.hits();
        if (CollectionUtil.isNotEmpty(hitList)) {
            List<AlbumInfoIndexVo> albumInfoIndexVoList = hitList.stream()
                    .map(hit -> {
                        //3.1 获取原始文档内容
                        AlbumInfoIndex albumInfoIndex = hit.source();

                        //3.2 获取高亮内容
                        Map<String, List<String>> highlightMap = hit.highlight();
                        if (CollectionUtil.isNotEmpty(highlightMap)) {
                            String albumTitleHighlight = highlightMap.get("albumTitle").get(0);
                            albumInfoIndex.setAlbumTitle(albumTitleHighlight);
                        }

                        //3.3 将索引库对象转为VO对象
                        return BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class);

                    })
                    .collect(Collectors.toList());
            vo.setList(albumInfoIndexVoList);

        }

        //4.响应VO


        return vo;
    }


    /**
     * 基于入参查询条件封装站内搜索完整的DSL语句
     *
     * @param albumIndexQuery 查询条件对象
     * @return 检索请求对象
     */
    private SearchRequest buildDSL(AlbumIndexQuery albumIndexQuery) {
        //1.创建检索请求构建器对象，作用：封装DSL语句完整请求。
        SearchRequest.Builder builder = new SearchRequest.Builder();

        //2.设置检索索引库名称
        builder.index(INDEX_NAME);

        //3.todo 设置查询条件 请求体参数"query"包含三大条件：关键字、标签条件
        String keyword = albumIndexQuery.getKeyword();
        //3.1 创建封装所有查询条件bool查询对象
        BoolQuery.Builder allConditonQueryBuilder = new BoolQuery.Builder();

        //3.2 设置第一大条件：关键字
        if (StringUtils.isNotBlank(keyword)) {
//            allConditonQueryBuilder.must(m -> {
//                m.bool(b -> {
//                    b.should(s -> {
//                                s.match(m1 -> {
//                                    m1.field("albumTitle").query(keyword);
//                                    return m1;
//                                });
//                                return s;
//                            })
//                            .should(s -> {
//                                s.match(m1 -> {
//                                    m1.field("albumIntro").query(keyword);
//                                    return m1;
//                                });
//                                return s;
//                            })
//                            .should(s -> {
//                                s.term(t -> {
//                                    t.field("announcerName").value(keyword);
//                                    return t;
//                                });
//                                return s;
//                            });
//                    return b;
//                });
//                return m;
//            });

            allConditonQueryBuilder.must(m -> m.bool(
                            b -> b.should(s -> s.match(m1 -> m1.field("albumTitle").query(keyword)))
                                    .should(s -> s.match(m1 -> m1.field("albumIntro").query(keyword)))
                                    .should(s -> s.term(t -> t.field("announcerName").value(keyword)))
                    )
            );
        }

        //3.3 设置第一大条件：分类 精确查询
        Long category1Id = albumIndexQuery.getCategory1Id();
        if (category1Id != null) {
//            allConditonQueryBuilder.filter(f -> {
//                f.term(t -> {
//                    t.field("category1Id").value(category1Id);
//                    return t;
//                });
//                return f;
//            });

            allConditonQueryBuilder.filter(f ->
                    f.term(t -> t.field("category1Id").value(category1Id))
            );
        }

        Long category2Id = albumIndexQuery.getCategory2Id();
        if (category2Id != null) {
//            allConditonQueryBuilder.filter(f -> {
//                f.term(t -> {
//                    t.field("category2Id").value(category2Id);
//                    return t;
//                });
//                return f;
//            });

            allConditonQueryBuilder.filter(f ->
                    f.term(t -> t.field("category2Id").value(category2Id))
            );
        }

        Long category3Id = albumIndexQuery.getCategory3Id();
        if (category3Id != null) {
//            allConditonQueryBuilder.filter(f -> {
//                f.term(t -> {
//                    t.field("category3Id").value(category3Id);
//                    return t;
//                });
//                return f;
//            });

            allConditonQueryBuilder.filter(f ->
                    f.term(t -> t.field("category3Id").value(category3Id))
            );
        }

        //3.4 设置第一大条件：标签 一组标签字符串，标签id:标签值id
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if (CollectionUtil.isNotEmpty(attributeList)) {
            for (String attribute : attributeList) {
                String[] split = attribute.split(":");
                if (split != null && split.length == 2) {
//                    allConditonQueryBuilder.filter(f -> {
//                        f.nested(n -> {
//                            n.path("attributeValueIndexList").query(
//                                    q -> {
//                                        q.bool(b -> {
//                                            b.must(m -> {
//                                                m.term(t -> {
//                                                    t.field("attributeValueIndexList.attributeId").value(split[0]);
//                                                    return t;
//                                                });
//                                                return m;
//                                            }).must(m -> {
//                                                m.term(t -> {
//                                                    t.field("attributeValueIndexList.valueId").value(split[1]);
//                                                    return t;
//                                                });
//                                                return m;
//                                            });
//                                            return b;
//                                        });
//                                        return q;
//                                    }
//                            );
//                            return n;
//                        });
//                        return f;
//                    });
                    allConditonQueryBuilder.filter(f ->
                            f.nested(n ->
                                    n.path("attributeValueIndexList")
                                            .query(q ->
                                                    q.bool(b ->
                                                            b.must(m -> m.term(t -> t.field("attributeValueIndexList.attributeId").value(split[0])))
                                                                    .must(m -> m.term(t -> t.field("attributeValueIndexList.valueId").value(split[1])))
                                                    )
                                            )
                            )
                    );


                }

            }

        }


        //3.x 将查询构建器对象转换为查询对象
        builder.query(allConditonQueryBuilder.build()._toQuery());

//        builder.query();

        //4.设置分页 请求体参数"from"和"size"
        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();
        int from = (pageNo - 1) * pageSize;

        builder.from(from).size(pageSize);

        //5.设置高亮 当前存在关键字条件，设置请求体参数"highlight"
        if (StringUtils.isNotBlank(keyword)) {
//            builder.highlight(h -> {
//                h.fields("albumTitle", t -> {
//                    t.preTags("<font color='red'>");
//                    t.postTags("</font>");
//                    return t;
//                });
//                return h;
//            });

            builder.highlight(h ->
                    h.fields("albumTitle", t ->
                            t.preTags("<font style='color:red'>")
                                    .postTags("</font>")
                    )
            );

        }

        //6.设置排序，设置请求体参数"sort" 前后端约定排序参数值，排序字段：排序方式
        String order = albumIndexQuery.getOrder();
        if (StringUtils.isNotBlank(order)) {
            String[] split = order.split(":");
            if (split != null && split.length == 2) {
                String orderField = "";
                if ("1".equals(split[0])) {
                    orderField = "hotScore";
                } else if ("2".equals(split[0])) {
                    orderField = "playStatNum";
                } else if ("3".equals(split[0])) {
                    orderField = "createTime";
                }

                SortOrder sortOrder = split[1].equals("asc") ? SortOrder.Asc : SortOrder.Desc;
                String finalOrderField = orderField;
//                builder.sort(s -> {
//                    s.field(f -> {
//                        f.field(finalOrderField).order(sortOrder);
//                        return f;
//                    });
//                    return s;
//                });

                builder.sort(s ->
                        s.field(f -> f.field(finalOrderField).order(sortOrder))
                );
            }
        }

        //7.指定响应字段，设置请求体参数"_source"
//        builder.source(s -> {
//            s.filter(f -> {
//                f.includes("id",
//                        "albumTitle",
//                        "albumIntro",
//                        "payType",
//                        "includeTrackCount",
//                        "playStatNum",
//                        "coverUrl");
//                return f;
//            });
//            return s;
//        });

        builder.source(s ->
                s.filter(f ->
                        f.includes(
                                "id", "albumTitle", "albumIntro", "payType",
                                "includeTrackCount", "playStatNum", "coverUrl",
                                "category1Id", "category2Id", "category3Id"
                        )
                )
        );


        //x.基于构建器对象得到检索对象请求。
        return builder.build();

    }
}
